package twoD.IsoPfem;

import java.util.ArrayList;

import inf.jlinalg.ArrayVector;
import inf.jlinalg.IVector;
import inf.jlinalg.Vector2D;
import inf.jlinalg.Vector3D;
import twoD.IsoPfem.Structure.CType;

public abstract class Element implements IElement {

	public static final double TOLERANCE_ABSOLUTE = 1e-10;
	public static final double TOLERANCE_RELATIVE = 1e-6;

	protected ArrayList<Integer> dofNumbers_;

	private int id;
	private RealTable realTable_;
	protected CType cType;

	private IGeometry G_;

	public void setId(int i) {
		this.id = i;
	}

	public int getId() {
		return this.id;
	}

	public void setRealTable(RealTable realTable) {
		this.realTable_ = realTable;
	}

	public void setCType(CType c) {
		this.cType = c;
	}

	public void enumerateDOFs(int d) {
		dofNumbers_ = G_.collectDofNumbers(d);
	}

	public ArrayList<Integer> getDOFNumbers() {
		return dofNumbers_;
	}

	public RealTable getRealTable() {
		return realTable_;
	}

	protected IVector getXe(int d) {
		int numNode = getNumberOfNodes();
		IVector Xe = new ArrayVector(d * numNode);
		for (int i = 0; i < numNode; i++) {
			double[] xei = getNodes(i).getPosition().toArray();
			for (int j = 0; j < d; j++)
				Xe.set(d * i + j, xei[j]);
		}
		/*
		 * TODO: to be rewritten to account for mode on edges & faces
		 */
		return Xe;
	}

	protected IVector getUe(int d) {
		int numNode = getNumberOfNodes();
		int numEdge = getNumberOfEdges();
		int edgeMode = numEdge == 0 ? 0 : getEdges(0).getNumberOfModes();
		int numFace = getNumberOfFaces();
		int faceMode = numFace == 0 ? 0 : getFaces(0).getNumberOfModes();
		int n = numNode + numEdge * edgeMode + numFace * faceMode;
		IVector Ue = new ArrayVector(d * n);
		for (int i = 0; i < numNode; i++) {
			Vector2D di = getNodes(i).getDisplacement();
			double[] uei = (di == null ? new double[d] : di.toArray());
			for (int j = 0; j < d; j++)
				Ue.set(d * i + j, uei[j]);
		}
		for (int i = 0; i < numEdge; i++) {
			int start = d * numNode + d * edgeMode * i;
			for (int j = 0; j < edgeMode; j++) {
				Vector2D di = getEdges(i).getDisplacement(j);
				double[] uei = (di == null ? new double[d] : di.toArray());
				for (int k = 0; k < d; k++)
					Ue.set(start + d * j + k, uei[k]);
			}
		}
		for (int i = 0; i < numFace; i++) {
			int start = d * numNode + d * edgeMode * numEdge + d * faceMode * i;
			for (int j = 0; j < faceMode; j++) {
				Vector2D di = getFaces(i).getDisplacement(j);
				double[] uei = (di == null ? new double[d] : di.toArray());
				for (int k = 0; k < d; k++)
					Ue.set(start + d * j + k, uei[k]);
			}
		}
		return Ue;
	}

	/**
	 * return the center of gravity of the element
	 */
	public Vector3D getCog() {
		int numNode = getNumberOfNodes();
		Vector2D cog = new Vector2D(0.0, 0.0);
		for (int i = 0; i < numNode; i++)
			cog = cog.add(getNodes(i).getPosition());
		cog = cog.multiply(1.0 / numNode);
		return new Vector3D(cog.getX1(), cog.getX2(), 0.0);
	}

	/**
	 * @return the meximum distance between 2 points in the element (i.e
	 *         diameter)
	 */
	@Override
	public double getDiameter() {
		int numNode = getNumberOfNodes();
		double d = 0;
		for (int i = 0; i < numNode; i++)
			for (int j = i; j < numNode; j++) {
				double tmp = getNodes(i).getPosition()
						.subtract(getNodes(j).getPosition()).norm2();
				if (tmp > d)
					d = tmp;
			}
		return d;
	}

	@Override
	public Node getNodes(int i) {
		return G_.getNodes(i);
	}

	@Override
	public ArrayList<Node> getNodes() {
		return G_.getNodes();
	}

	@Override
	public int getNumberOfNodes() {
		return G_.getNumberOfNodes();
	}

	@Override
	public IEdge getEdges(int idx) {
		return G_.getEdges(idx);
	}

	@Override
	public ArrayList<IEdge> getEdges() {
		return G_.getEdges();
	}

	@Override
	public int getNumberOfEdges() {
		return G_.getNumberOfEdges();
	}

	@Override
	public IFace getFaces(int idx) {
		return G_.getFaces(idx);
	}

	@Override
	public ArrayList<IFace> getFaces() {
		return G_.getFaces();
	}

	@Override
	public int getNumberOfFaces() {
		return G_.getNumberOfFaces();
	}

	@Override
	public IGeometry getGeometry() {
		return G_;
	}

	@Override
	public void setGeometry(IGeometry G) {
		G_ = G;
	}
}
